// SPDX-License-Identifier: MIT
/*
 * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. All rights reserved.
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 */

#include <stdint.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <stdbool.h>
#include <cdma_abi.h>
#include "cdma_u_log.h"
#include "cdma_u_cmd.h"
#include "cdma_u_jfs.h"
#include "cdma_u_common.h"

static int cdma_rw_check(struct dma_seg *rmt_seg, struct dma_seg *local_seg)
{
	if (rmt_seg->len == 0 || local_seg->len == 0) {
		CDMA_LOG_ERR("segment check invalid len.\n");
		return -EINVAL;
	}

	if (rmt_seg->sva == 0 || local_seg->sva == 0) {
		CDMA_LOG_ERR("segment check invalid address.\n");
		return -EINVAL;
	}

	return 0;
}

static inline void cdma_fill_comm_wr(dma_jfs_wr_t *wr, struct dma_queue *queue)
{
	struct cdma_u_queue *cdma_queue = to_cdma_u_queue(queue);

	wr->flag.bs.complete_enable = 1;
	wr->flag.bs.fence = 1;
	wr->user_ctx = queue->cfg.user_ctx;
	if (cdma_queue->cdma_tp)
		wr->tpn = cdma_queue->cdma_tp->tpn;
	wr->rmt_eid = queue->cfg.rmt_eid.dw0;
	wr->next = NULL;
}

static void cdma_fill_sge(dma_sge_t *rmt_sge, dma_sge_t *local_sge,
			  struct dma_seg *rmt_seg, struct dma_seg *local_seg)
{
	local_sge->addr = local_seg->sva;
	local_sge->len = local_seg->len;
	local_sge->seg = *local_seg;

	rmt_sge->addr = rmt_seg->sva;
	rmt_sge->len = rmt_seg->len;
	rmt_sge->seg = *rmt_seg;
}

dma_status cdma_cas(struct dma_queue *queue, struct dma_seg *rmt_seg,
		    struct dma_seg *local_seg, uint64_t cmp, uint64_t swap)
{
	dma_jfs_wr_t wr = {.opcode = CDMA_WR_OPC_CAS};
	struct cdma_u_queue *cdma_queue;
	dma_sge_t rmt_sge, local_sge;
	dma_jfs_wr_t *bad_wr = NULL;
	int ret;

	if (cdma_rw_check(rmt_seg, local_seg)) {
		CDMA_LOG_ERR("cas param check failed.\n");
		return DMA_STATUS_INVAL;
	}

	cdma_fill_comm_wr(&wr, queue);

	cdma_fill_sge(&rmt_sge, &local_sge, rmt_seg, local_seg);

	wr.cas.src = &local_sge;
	wr.cas.dst = &rmt_sge;

	if (local_sge.len <= CDMA_ATOMIC_LEN_8) {
		wr.cas.cmp_data = cmp;
		wr.cas.swap_data = swap;
	} else {
		wr.cas.cmp_addr = cmp;
		wr.cas.swap_addr = swap;
	}

	cdma_queue = to_cdma_u_queue(queue);
	ret = cdma_u_post_jfs_wr(cdma_queue->cdma_jfs, &wr, &bad_wr);
	if (ret) {
		CDMA_LOG_ERR("post jfs for cas failed, ret = %d.\n", ret);
		return DMA_STATUS_INVAL;
	}

	return DMA_STATUS_OK;
}

dma_status cdma_faa(struct dma_queue *queue, struct dma_seg *rmt_seg,
		    struct dma_seg *local_seg, uint64_t operand)
{
	dma_jfs_wr_t wr = {.opcode = CDMA_WR_OPC_FADD};
	struct cdma_u_queue *cdma_queue;
	dma_sge_t rmt_sge, local_sge;
	dma_jfs_wr_t *bad_wr = NULL;
	int ret;

	if (cdma_rw_check(rmt_seg, local_seg)) {
		CDMA_LOG_ERR("faa param check failed.\n");
		return DMA_STATUS_INVAL;
	}

	cdma_fill_comm_wr(&wr, queue);

	cdma_fill_sge(&rmt_sge, &local_sge, rmt_seg, local_seg);

	wr.faa.src = &local_sge;
	wr.faa.dst = &rmt_sge;
	wr.faa.operand = operand;

	cdma_queue = to_cdma_u_queue(queue);
	ret = cdma_u_post_jfs_wr(cdma_queue->cdma_jfs, &wr, &bad_wr);
	if (ret) {
		CDMA_LOG_ERR("post jfs for faa failed, ret = %d.\n", ret);
		return DMA_STATUS_INVAL;
	}

	return DMA_STATUS_OK;
}

dma_status cdma_write(struct dma_queue *queue, struct dma_seg *rmt_seg,
		      struct dma_seg *local_seg, struct dma_seg *notify_seg,
		      uint64_t notify_data)
{
	dma_jfs_wr_t wr = {.opcode = CDMA_WR_OPC_WRITE};
	struct cdma_u_queue *cdma_queue;
	dma_sge_t rmt_sge, local_sge;
	dma_jfs_wr_t *bad_wr = NULL;
	int ret;

	if (cdma_rw_check(rmt_seg, local_seg)) {
		CDMA_LOG_ERR("write param check failed.\n");
		return DMA_STATUS_INVAL;
	}

	if (notify_seg) {
		wr.opcode = CDMA_WR_OPC_WRITE_NOTIFY;
		wr.rw.notify_addr = notify_seg->sva;
		wr.rw.notify_data = notify_data;
		wr.rw.notify_tokenid = notify_seg->tid;
		wr.rw.notify_tokenvalue = notify_seg->token_value;
	}

	cdma_fill_comm_wr(&wr, queue);

	cdma_fill_sge(&rmt_sge, &local_sge, rmt_seg, local_seg);

	wr.rw.src.num_sge = 1;
	wr.rw.src.sge = &local_sge;

	wr.rw.dst.num_sge = 1;
	wr.rw.dst.sge = &rmt_sge;

	cdma_queue = to_cdma_u_queue(queue);
	ret = cdma_u_post_jfs_wr(cdma_queue->cdma_jfs, &wr, &bad_wr);
	if (ret) {
		CDMA_LOG_ERR("post jfs for write failed, ret = %d.\n", ret);
		return DMA_STATUS_INVAL;
	}

	return DMA_STATUS_OK;
}

dma_status cdma_read(struct dma_queue *queue, struct dma_seg *rmt_seg,
		     struct dma_seg *local_seg)
{
	dma_jfs_wr_t wr = {.opcode = CDMA_WR_OPC_READ};
	struct cdma_u_queue *cdma_queue;
	dma_sge_t rmt_sge, local_sge;
	dma_jfs_wr_t *bad_wr = NULL;
	int ret;

	if (cdma_rw_check(rmt_seg, local_seg)) {
		CDMA_LOG_ERR("read param check failed.\n");
		return DMA_STATUS_INVAL;
	}

	cdma_fill_comm_wr(&wr, queue);

	cdma_fill_sge(&rmt_sge, &local_sge, rmt_seg, local_seg);

	wr.rw.src.num_sge = 1;
	wr.rw.src.sge = &rmt_sge;

	wr.rw.dst.num_sge = 1;
	wr.rw.dst.sge = &local_sge;

	cdma_queue = to_cdma_u_queue(queue);
	ret = cdma_u_post_jfs_wr(cdma_queue->cdma_jfs, &wr, &bad_wr);
	if (ret) {
		CDMA_LOG_ERR("post jfs for read failed, ret = %d.\n", ret);
		return DMA_STATUS_INVAL;
	}

	return DMA_STATUS_OK;
}

int cdma_cmd_create_jfce(struct dma_context *ctx, dma_jfce_t *jfce)
{
	struct cdma_cmd_create_jfce_args arg = {0};
	struct cdma_ioctl_hdr hdr = {0};
	int ret;

	if (ctx == NULL || ctx->dma_dev == NULL || ctx->dma_dev->fd < 0) {
		CDMA_LOG_ERR("create jfce cmd parameter invalid.\n");
		return -EINVAL;
	}

	hdr.command = (uint32_t)CDMA_CMD_CREATE_JFCE;
	hdr.args_len = (uint32_t)sizeof(arg);
	hdr.args_addr = (uint64_t)&arg;

	ret = ioctl(ctx->dma_dev->fd, CDMA_SYNC, &hdr);
	if (ret != 0) {
		CDMA_LOG_ERR("ioctl execute create jfce failed, ret = %d.\n", ret);
		return ret;
	}

	jfce->fd = arg.out.fd;
	jfce->id = arg.out.id;

	return 0;
}

static inline void cdma_cmd_set_udrv_priv(struct cdma_cmd_udrv_priv *arg,
					  struct cdma_cmd_udrv_priv *udata)
{
	if (arg != NULL && udata != NULL) {
		*arg = *udata;
	}
}

int cdma_cmd_create_ctp(struct dma_context *ctx, struct dma_tp *ctp,
			struct dma_tp_cfg *cfg)
{
	struct cdma_cmd_create_ctp_args arg = {0};
	struct cdma_ioctl_hdr hdr;
	int ret;

	if (!ctx || !ctx->dma_dev || ctx->dma_dev->fd < 0 || !ctp || !cfg) {
		CDMA_LOG_ERR("create ctp cmd parameter invalid.\n");
		return -EINVAL;
	}

	hdr.command = (uint32_t)CDMA_CMD_CREATE_CTP;
	hdr.args_len = (uint32_t)sizeof(arg);
	hdr.args_addr = (uint64_t)&arg;

	arg.in.scna = cfg->scna;
	arg.in.dcna = cfg->dcna;
	arg.in.seid = cfg->seid;
	arg.in.deid = cfg->deid;
	arg.in.dma_tp = (uint64_t)(void *)ctp;
	arg.in.queue_id = cfg->queue_id;

	ret = ioctl(ctx->dma_dev->fd, CDMA_SYNC, &hdr);
	if (ret != 0) {
		CDMA_LOG_ERR("ioctl execute create ctp failed, ret = %d, errno = %d, cmd = %u.\n",
			      ret, errno, hdr.command);
		return ret;
	}

	ctp->dma_ctx = ctx;
	ctp->cfg = *cfg;
	ctp->tpn = arg.out.tpn;
	ctp->handle = arg.out.handle;

	return 0;
}

int cdma_cmd_delete_ctp(struct dma_tp *ctp)
{
	struct cdma_cmd_delete_ctp_args arg = {0};
	struct cdma_ioctl_hdr hdr;
	int ret;

	if (!ctp || !ctp->dma_ctx || !ctp->dma_ctx->dma_dev ||
		ctp->dma_ctx->dma_dev->fd < 0) {
		CDMA_LOG_ERR("cdma delete ctp cmd parameter invalid.\n");
		return -EINVAL;
	}

	hdr.command = (uint32_t)CDMA_CMD_DELETE_CTP;
	hdr.args_len = (uint32_t)sizeof(arg);
	hdr.args_addr = (uint64_t)&arg;
	arg.in.tpn = ctp->tpn;
	arg.in.handle = ctp->handle;
	arg.in.queue_id = ctp->cfg.queue_id;

	ret = ioctl(ctp->dma_ctx->dma_dev->fd, CDMA_SYNC, &hdr);
	if (ret != 0) {
		CDMA_LOG_ERR("ioctl execute delete ctp failed, ret = %d, errno = %d, cmd = %u.\n",
			      ret, errno, hdr.command);
		return ret;
	}

	return 0;
}

static inline void cdma_fill_jfs(struct dma_jfs *jfs, struct dma_context *ctx,
				 struct dma_jfs_cfg *cfg,
				 struct cdma_cmd_create_jfs_args *arg)
{
	jfs->jfs_id = arg->out.id;
	jfs->jfs_cfg = *cfg;
	jfs->jfs_cfg.depth = arg->out.depth;
	jfs->jfs_cfg.max_sge = arg->out.max_sge;
	jfs->jfs_cfg.max_rsge = arg->out.max_rsge;
	jfs->dma_ctx = ctx;
	jfs->handle = arg->out.handle;
}

int cdma_u_cmd_create_jfs(struct dma_context *ctx, struct dma_jfs *jfs,
			  struct dma_jfs_cfg *cfg,
			  struct cdma_cmd_udrv_priv *udata)
{
	struct cdma_cmd_create_jfs_args arg = {0};
	struct cdma_ioctl_hdr hdr;
	int ret;

	if (!ctx || !ctx->dma_dev || ctx->dma_dev->fd < 0 || !jfs ||
		!cfg || !cfg->jfc) {
		CDMA_LOG_ERR("create jfs cmd parameter invalid.\n");
		return -EINVAL;
	}

	hdr.command = (uint32_t)CDMA_CMD_CREATE_JFS;
	hdr.args_len = (uint32_t)sizeof(arg);
	hdr.args_addr = (uint64_t)&arg;

	arg.in.depth = cfg->depth;
	arg.in.flag = cfg->flag.value;
	arg.in.priority = cfg->priority;
	arg.in.max_sge = cfg->max_sge;
	arg.in.max_rsge = cfg->max_rsge;
	arg.in.rnr_retry = cfg->rnr_retry;
	arg.in.err_timeout = cfg->err_timeout;
	arg.in.jfc_id = cfg->jfc->jfc_id;
	arg.in.queue_id = cfg->queue_id;
	arg.in.rmt_eid = cfg->rmt_eid;
	arg.in.pld_token_id = cfg->pld_token_id;
	arg.in.tpn = cfg->tpn;
	arg.in.dma_jfs = (uint64_t)jfs;
	arg.in.eid_idx = cfg->eid_idx;
	arg.in.trans_mode = cfg->trans_mode;
	cdma_cmd_set_udrv_priv(&arg.udata, udata);

	ret = ioctl(ctx->dma_dev->fd, CDMA_SYNC, &hdr);
	if (ret != 0) {
		CDMA_LOG_ERR("ioctl execute create jfs failed, ret = %d, cmd = %u.\n",
			     ret, hdr.command);
		return -ret;
	}

	cdma_fill_jfs(jfs, ctx, cfg, &arg);

	return 0;
}

int cdma_u_cmd_delete_jfs(struct dma_jfs *jfs)
{
	struct cdma_cmd_delete_jfs_args arg = {0};
	struct cdma_ioctl_hdr hdr;
	int ret;

	if (!jfs || !jfs->dma_ctx || !jfs->dma_ctx->dma_dev ||
		jfs->dma_ctx->dma_dev->fd < 0) {
		CDMA_LOG_ERR("delete jfs cmd parameter invalid");
		return -EINVAL;
	}

	hdr.command = (uint32_t)CDMA_CMD_DELETE_JFS;
	hdr.args_len = (uint32_t)sizeof(arg);
	hdr.args_addr = (uint64_t)&arg;
	arg.in.jfs_id = jfs->jfs_id;
	arg.in.queue_id = jfs->jfs_cfg.queue_id;
	arg.in.handle = jfs->handle;

	ret = ioctl(jfs->dma_ctx->dma_dev->fd, CDMA_SYNC, &hdr);
	if (ret != 0) {
		CDMA_LOG_ERR("ioctl execute delete jfs failed, ret = %d, cmd = %u.\n",
			     ret, hdr.command);
		return -EFAULT;
	}

	return 0;
}

